import { _decorator, Animation, AnimationClip, Component, EventTouch, instantiate, Label, Node, Prefab, profiler, Sprite, SpriteAtlas, SpriteFrame, Tween, tween, UITransform, v3, Vec2, Vec3, Widget } from 'cc';
import { BaseGame } from './BaseGame';
import { GameState } from '../managers/GameManager';
import { Dish } from './common/Dish';
import { Line } from './MergeDish/Line';
import { _game, _item, _platform, _player, _resource, _wheel } from '../Main';
import { AudioName, PanelName } from '../managers/ResourceManager';
import { Customer } from './MergeDish/Customer';
import { Task } from './MergeDish/Task';
import { Fail } from './MergeDish/Fail';
import { TT } from '../platforms/minis/TT';
const { ccclass, property } = _decorator;

@ccclass('MergeDish')
export class MergeDish extends BaseGame {

    gameId: number = 1

    gameName: string = '合成食物'

    @property(Node)
    ui: Node = null!

    @property(Node)
    zone: Node = null!

    @property(Prefab)
    dishPrefab: Prefab = null!

    @property([Prefab])
    linePrefabs: Prefab[] = []

    @property(Node)
    table: Node = null!

    @property(Node)
    servedNode: Node = null!

    @property(Prefab)
    customerPrefab: Prefab = null!

    @property(Prefab)
    taskPrefab: Prefab = null!

    @property(Node)
    seats: Node = null!

    @property(Node)
    waitings: Node = null!

    @property(Node)
    reachNode: Node = null!

    @property(Label)
    reach: Label = null!

    @property(Prefab)
    unlockPanel: Prefab = null!

    @property(Prefab)
    mergeEffect: Prefab = null!

    @property(Prefab)
    uncoverEffect: Prefab = null!

    @property(Node)
    effects: Node = null!

    @property(Node)
    bottom: Node = null!

    @property(Prefab)
    transPrefab: Prefab = null!

    @property(Fail)
    failSc: Fail = null!

    complainTimer: number = null

    passtimeTimer: number = null

    passtime: number = 0

    complains = [
        '我好饿啊', '什么时候到我', '快点快点', '求求你快点', '有飞碟', '饿死我啦', '好想干饭',
        '这个能吃吗', '饿出幻觉了', '快让我吃一口', '好香啊', '受不了啦', '老板我想吃饭', '真能到我吗',
        '感觉好好吃', '搞快点搞快点', '能快点吗', '饿过劲了', '肚子在叫哦', '真的很饿啊', '都有啥吃的啊',
        '啥时候到我', '快啦快啦', '先让我吃一口', '能插队吗', '让我先来吧', '谁挤我呢', '想吃好吃的',
        '别挤别挤', '闻起来不错啊', '看着就有食欲', '能不能快点', '饿啊', '谁踩我一脚', '挤到我啦'
    ]

    flyTime: number = 0.2

    moveTime: number = 0.1

    level: number = 0

    touchable: boolean = true

    touched = null

    maxType: number = 10

    maxSlot: number = 8

    defSlot: number = 7

    curSlot: number = this.defSlot

    slotPoss: Vec3[] = []

    served: Dish[] = []

    customers: Customer[] = [null, null, null]

    customerWaitings: Customer[][] = [[], [], []]

    servedNumber: number = 0

    dishScale: number = 0.65

    patienceChance: number = 10

    stackChances = [
        [100, 0, 0],
        [70, 20, 10],
        [50, 30, 20],
        [40, 40, 20],
        [30, 40, 30],
        [20, 30, 50],
        [10, 30, 60],
    ]

    taskChances = [
        [100, 0, 0],
        [0, 100, 0],
        [0, 0, 100]
    ]

    propsUse = {
        refresh: 0,
        clear: 0,
        uncover: 0,
        revive: 0,
        unlock: 0
    }

    types: number[] = []

    init(): void {
        _player.switchBgm(AudioName.gamebgm)
        this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this)
        this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this)
        this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this)
        this.initSlots()
        this.refreshUI()
        this.runTimers()
        _game.loading = false

        this.run()
    }

    isTrial(): boolean {
        return this.level == 0
    }

    refreshUI() {
        let sysInfo = _platform.platform.systemInfo
        if (!sysInfo || !sysInfo.safeArea) {
            return
        }
        let height: number = sysInfo.screenHeight
        let ratio: number = height / 667
        let top: Node = this.ui.getChildByName('Top')
        let wt: Widget = top.getComponent(Widget)
        let wbottom: number = sysInfo.safeArea.height * sysInfo.pixelRatio - 50
        wt.bottom = wbottom

        // let ty: number = height - 447 * ratio
        // this.seats.position = v3(0, ty + 200, 0)
        // this.table.position = v3(0, ty, 0)
        this.initSlots()
    }

    createTrans() {
        let node: Node = instantiate(this.transPrefab)
        node.parent = this.node
        tween(node)
            .delay(2)
            .call(() => {
                node.removeFromParent()
            })
            .start()
    }

    clearTimers() {
        if (this.complainTimer) {
            clearInterval(this.complainTimer)
            this.complainTimer = null
        }
        if (this.passtimeTimer) {
            clearInterval(this.passtimeTimer)
            this.passtimeTimer = null
        }
    }

    runTimers() {
        this.clearTimers()
        this.complainTimer = setInterval(() => {
            this.checkComplain()
        }, 2000)
        this.passtimeTimer = setInterval(() => {
            this.checkPasstime()
        }, 100)
    }

    checkComplain() {
        if (!this.isInGameState(GameState.Gaming) || this.isTrial()) {
            return
        }
        let cs = this.waitings.children
        if (cs.length <= 0) {
            return
        }
        let cn: Node = _wheel.getRandomItemInArray(cs)
        let customer: Customer = cn.getComponent(Customer)
        if (customer.complaining) {
            return
        }
        customer.doComplain(_wheel.getRandomItemInArray(this.complains))
    }

    checkPasstime() {
        if (!this.isInGameState(GameState.Gaming) || this.isTrial()) {
            return
        }
        this.passtime += 100
    }

    initSlots() {
        this.slotPoss = []
        let ty: number = this.table.getPosition().y
        let ss = this.table.getChildByName('Slots')
        let ssy: number = ss.getPosition().y
        let slots = ss.children
        for (let i = 0; i < slots.length; i++) {
            let sn: Node = slots[i]
            let x: number = sn.getPosition().x
            this.slotPoss.push(v3(x, ty + ssy, 0))
        }
    }

    refreshSlots() {
        let slots = this.table.getChildByName('Slots').children
        for (let i = 0; i < slots.length; i++) {
            let sn: Node = slots[i]
            let ln: Node = sn.getChildByName('Lock')
            ln.active = this.curSlot <= i
        }
    }

    onTouchStart(event: EventTouch) {
        if (this.gameState != GameState.Gaming || !this.touchable) {
            return
        }
        let pos: Vec2 = event.getUILocation()
        let ld = this.getLineDishByWorldPos(pos)
        if (!ld || !ld.dish || !ld.dish.touchable) {
            return
        }
        this.touched = ld.dish
    }

    onTouchEnd(event: EventTouch) {
        if (this.gameState != GameState.Gaming || !this.touchable) {
            return
        }
        if (this.touched == null) {
            return
        }
        let pos: Vec2 = event.getUILocation()
        let ld = this.getLineDishByWorldPos(pos)
        if (!ld || !ld.dish || !ld.dish.touchable || ld.dish != this.touched) {
            return
        }
        this.serveDish(ld.line, ld.dish)
        this.touched = null
    }

    serveDish(line: Line, dish: Dish) {
        let length: number = this.served.length
        if (length >= this.curSlot) {
            return
        }
        _player.doShake('short')
        _player.playSound(AudioName.clickdish)
        //获取传送带
        let ln: Node = line.node
        let lpos: Vec3 = ln.getPosition()
        //获取老餐盘
        let dn: Node = dish.node
        let dpos: Vec3 = dn.getPosition()
        //移除老餐盘并生成新餐盘
        let ndn: Node = instantiate(this.dishPrefab)
        ndn.parent = this.servedNode
        let npos: Vec3 = v3(lpos.x + dpos.x, lpos.y + dpos.y, 0)
        ndn.position = npos
        //播放动画
        if (dish.covered) {
            this.playUncoverEffect(npos.add3f(0, 3, 0))
        }
        let ndish: Dish = ndn.getComponent(Dish)
        ndish.init(dish.type, dish.index, false, dish.floor)
        dn.removeFromParent()
        //获取新餐盘位置顺位
        let sames: number[] = []
        let index: number = length
        for (let i = 0; i < length; i++) {
            let cdish: Dish = this.served[i]
            if (cdish.removed || cdish.merged) {
                continue
            }
            if (ndish.type == cdish.type) {
                sames.push(i)
                index = i + 1
                if (sames.length == 2) {
                    sames.push(i + 1)
                    break
                }
            }
        }
        //将餐盘添加到缓存内
        this.served.splice(index, 0, ndish)
        //餐盘飞向目标位置
        this.moveToTable(ndn, ndish, index, sames)
        //传送带刷新并移动
        if (!this.isTrial()) {
            if (ndish.floor === 0) {
                let ds = line.getDishesByFloor(0)
                if (ds.length < line.full) {
                    this.createDishes(line, this.getStackNumberByPhase())
                    line.changeDishState()
                }
                line.moveDishes(ndish.index)
            } else {
                line.changeDishState()
            }
        } else {
            if (ndish.floor > 0) {
                line.changeDishState()
            }
        }
        //防止连点
        this.touchable = false
        tween(this)
            .delay(this.moveTime + 0.01)
            .call(() => {
                this.touchable = true
            })
            .start()
    }

    getServedDishSize(merged: boolean = false): number {
        let size = 0
        for (let i = 0; i < this.served.length; i++) {
            let dish: Dish = this.served[i]
            if (!merged) {
                if (!dish.merged) {
                    size += 1
                }
            } else {
                if (dish.merged) {
                    size += 1
                }
            }
        }
        return size 
    }

    moveToTable(dn: Node, dish: Dish, index: number, sames: number[]) {
        let moveTime: number = this.moveTime * 2
        this.moveToTablePos(dn, index, moveTime, () => {
            this.moveOthers(-1)
        })
        //如果有三个相同
        if (sames.length >= 3) {
            this.moveOthers(index)
            let findex: number = sames[0]
            let fdish: Dish = this.served[findex]
            let canMerge: boolean = this.canMerge(fdish.type)
            let removes: Node[] = []
            for (let i = sames.length - 1; i >= 0; i--) {
                let n: number = sames[i]
                let mdish: Dish = this.served[n]
                let md: Node = mdish.node
                mdish.removed = true
                if (canMerge) {
                    if (i != 0) {
                        this.served.splice(n, 1)
                        removes.push(md)
                    }
                } else {
                    this.served.splice(n, 1)
                    removes.push(md)
                }
            }
            tween(this)
                .delay(moveTime)
                .call(() => {
                    for (let i = 0; i < removes.length; i++) {
                        let md: Node = removes[i]
                        this.moveToTablePos(md, findex, this.moveTime, () => {
                            md.removeFromParent()
                            if (canMerge) {
                                fdish.merge()
                            }
                        })
                    }
                    this.moveOthers(canMerge ? findex : findex - 1)
                    if (canMerge) {
                        this.scheduleOnce(() => {
                            fdish.merge()
                            this.checkCustomers()
                        }, this.moveTime + 0.01)
                    }
                    tween(this)
                        .delay(this.moveTime)
                        .call(() => {
                            _player.playSound(AudioName.hecheng)
                            this.playMergeEffect(this.slotPoss[findex], this.dishScale)
                        })
                        .start()
                })
                .start()
        } else {
            dish.pop()
            this.moveOthers(index)
            if (this.served.length >= this.curSlot) {
                this.fail()
            }
        }
    }

    moveOthers(index: number) {
        let length: number = this.served.length
        for (let i = index + 1; i < length; i++) {
            if (i >= length) {
                break
            }
            let sdish: Dish = this.served[i]
            if (!sdish) {
                continue
            }
            let sdn: Node = sdish.node
            this.moveToTablePos(sdn, i)
        }
    }

    moveToTablePos(dn: Node, index: number, time: number = this.moveTime, then?: () => void) {
        let pos: Vec3 = this.slotPoss[index]
        tween(dn)
            .to(time, { position: pos, scale: v3(this.dishScale, this.dishScale, this.dishScale) })
            .call(() => {
                if (then) {
                    then()
                }
            })
            .start()
    }

    canMerge(type: number): boolean {
        let count: number = 0
        for (let i = 0; i < this.customers.length; i++) {
            let customer: Customer = this.customers[i]
            if (!customer || !customer.sat) {
                continue
            }
            for (let j = 0; j < customer.tasks.length; j++) {
                let task: Task = customer.tasks[j]
                if (task.type != type) {
                    continue
                }
                count++
            }
        }
        for (let i = 0; i < this.served.length; i++) {
            let dish: Dish = this.served[i]
            if (dish.type != type || !dish.merged) {
                continue
            }
            count -= 1
        }
        return count > 0
    }

    checkCustomers() {
        let customers: Customer[] = []
        for (let i = 0; i < this.customers.length; i++) {
            let customer: Customer = this.customers[i]
            if (!customer || !customer.sat) {
                continue
            }
            if (customers.length <= 0) {
                customers.push(customer)
            } else {
                let check: Customer = customers[0]
                if (customer.tasks.length > check.tasks.length) {
                    customers.splice(0, 0, customer)
                } else {
                    customers.push(customer)
                }
            }
        }
        for (let i = 0; i < customers.length; i++) {
            let customer: Customer = customers[i]
            let checked = this.checkCustomer(customer)
            this.serveCustomer(customer, checked, true)
        }
        for (let i = 0; i < this.served.length; i++) {
            let dish: Dish = this.served[i]
            dish.checkForTask(dish.merged)
        }
    }

    /**
     * 获取符合顾客任务的食物数组
     * @param customer 顾客
     * @returns 食物数组
     */
    checkCustomer(customer: Customer): Dish[] {
        //创建检查数组
        let checked: Dish[] = []
        //循环客人所携带的任务
        for (let j = 0; j < customer.tasks.length; j++) {
            let task: Task = customer.tasks[j]
            task.changeFinished(false)
            //循环所有餐桌上的餐盘
            for (let k = 0; k < this.served.length; k++) {
                let dish: Dish = this.served[k]
                if (!dish.merged) {
                    continue
                }
                //同一类型并且未被其他任务占用
                if (task.type == dish.type) {
                    if (checked.indexOf(dish) < 0) {
                        task.changeFinished(true)
                        checked.push(dish)
                        // dish.checkForTask(true)
                        break
                    }
                }
            }
        }
        return checked
    }

    serveCustomer(customer: Customer, checked: Dish[], served: boolean) {
        if (served) {
            if (customer.getFinishedSize() != customer.tasks.length) {
                return
            }
        }
        //标记客人为服务完成
        customer.sat = false
        let seat: number = this.customers.indexOf(customer)
        this.customers[seat] = null
        //改变表情
        if (served) {
            customer.changePic(1)
        } else {
            customer.changePic(4)
        }
        //创建新的数组
        let newServed: Dish[] = []
        //定义飞行时间
        let cn: Node = customer.node
        let spos = this.seats.getPosition(), cpos = cn.getPosition(), ccpos = customer.getContentCenter()
        let tpos: Vec3 = v3(spos.x + cpos.x + ccpos.x, spos.y + ccpos.y, 0)
        for (let i = 0; i < this.served.length; i++) {
            let dish: Dish = this.served[i]
            //如果存在于检测数组
            if (checked.indexOf(dish) >= 0) {
                let dn: Node = dish.node
                //飞行到客人对话框后移除
                tween(dn)
                    .to(this.flyTime, { position: tpos, scale: v3(0.1, 0.1, 0.1) } ,{ easing: 'quadOut' })
                    .call(() => {
                        dn.removeFromParent()
                    })
                    .start()
                continue
            }
            newServed.push(dish)
        }
        //将新数组赋予给老数组
        this.served = newServed
        this.moveOthers(-1)
        //餐盘飞行到位后将客人移出同时创建新客人
        this.scheduleOnce(() => {
            customer.hideContent(true, () => {
                //客人移出屏幕
                tween(cn)
                    .to(.5, { position: v3(-600, 0, 0) }, { easing: 'quadOut' })
                    .call(() => {
                        cn.removeFromParent()
                        //刷新显示并生成新客人
                        if (!this.isTrial() && served) {
                            this.servedNumber += 1
                            this.refreshServed()
                        }
                        if (this.servedNumber < 30) {
                            if (!this.isTrial()) {
                                this.waitingMoveToSeat(seat, served)
                                this.checkCustomers()
                            } else {
                                this.reset()
                                this.runGame(1)
                            }
                        } else {
                            this.success()
                        }
                    })
                .start()
            })
        }, this.flyTime)
    }

    refreshServed() {
        let rn: Node = this.reach.node
        Tween.stopAllByTarget(rn)
        this.reach.string = this.servedNumber + ''
        tween(rn)
            .to(.3, { scale: v3(1.2, 1.2, 1.2) }, { easing: 'quadOut' })
            .to(.3, { scale: v3(1, 1, 1) }, { easing: 'quadIn' })
            .start()
    }

    getLineDishByWorldPos(pos: Vec2): { line: Line, dish: Dish } {
        let ls = this.zone.children
        for (let i = 0; i < ls.length; i++) {
            let ln: Node = ls[i]
            let line: Line = ln.getComponent(Line)
            if (!line.touchable) {
                continue
            }
            let ds = line.zone.children
            for (let j = ds.length - 1; j >= 0; j--) {
                let dn: Node = ds[j]
                let dish: Dish = dn.getComponent(Dish)
                if (!dish.touchable) {
                    continue
                }
                if (dn.getComponent(UITransform).getBoundingBoxToWorld().contains(pos)) {
                    return { line: line, dish: dish }
                }
            }
        }
        return null
    }

    run(): void {
        this.gameState = GameState.Loading
        this.reset()
        this.runGame()
        this.gameState = GameState.Gaming
        let pf = _platform.platform
        if (pf instanceof TT) {
            pf.reportAnalytics('Behavior', {
                purpose: '开始',
                type: '游戏',
                name: this.gameName,
                amount: 1,
                source: '主页',
            })
        }
    }

    runGame(level: number = 0) {
        this.level = level
        if (this.isTrial()) {
            // this.createTrialLines([1, 2])
            this.createTrial()
            this.createCustomer(1, 1, [1, 2])
        } else {
            this.createTrans()
            this.createLines()
            this.createCustomers()
        }
        this.reachNode.active = !this.isTrial()
    }

    clearZone() {
        let ls = this.zone.children
        for (let i = ls.length - 1; i >= 0; i--) {
            let ln: Node = ls[i]
            ln.removeFromParent()
        }
    }

    clearSeats() {
        let cs = this.seats.children
        for (let i = cs.length - 1; i >= 0; i--) {
            let cn: Node = cs[i]
            cn.removeFromParent()
        }
        let ws = this.waitings.children
        for (let i = ws.length - 1; i >= 0; i--) {
            let wn: Node = ws[i]
            wn.removeFromParent()
        }
        this.customers = [null, null, null]
        this.customerWaitings = [[], [], []]
        this.servedNumber = 0
        this.reach.string = this.servedNumber + ''
    }

    clearServed() {
        let ds = this.servedNode.children
        for (let i = ds.length - 1; i >= 0; i--) {
            let dn: Node = ds[i]
            dn.removeFromParent()
        }
        this.served = []
    }

    createLines() {
        this.createLine(5, 0, v3(-280, -250, 0))
        this.createLine(6, 0, v3(280, -250, 0))
        let pos: Vec3 = v3(0, -660, 0)
        this.createLine(7, 0, pos)
        this.bottom.position = pos
    }

    createTrialLines(types: [number, number] = [1, 2]) {
        let line1: Line = this.createLine(1, 0, v3(0, 0, 0))
        let line2: Line = this.createLine(1, 0, v3(0, -400, 0))
        let ts: number[] = []
        for (let i = 0; i < types.length; i++) {
            for (let j = 0; j < 3; j++) {
                ts.push(types[i])
            }
        }
        let length: number = ts.length / 2
        for (let i = 0; i < length; i++) {
            let r1: number = _wheel.getRandomInt(0, ts.length)
            let i1: number = ts[r1]
            ts.splice(r1, 1)
            this.createDish(line1, i1, 1 + 1.5 * i)
            let r2: number = _wheel.getRandomInt(0, ts.length)
            let i2: number = ts[r2]
            ts.splice(r2, 1)
            this.createDish(line2, i2, 1 + 1.5 * i)
        }
        let dishes: Dish[] = this.getCoveredDishes(false)
        let dish: Dish = _wheel.getRandomItemInArray(dishes)
        dish.changeCover(true)
        this.bottom.position = v3(0, -1000, 0)
    }

    createTrial() {
        let line1: Line = this.createLine(1, 0, v3(0, 0, 0))
        let line2: Line = this.createLine(1, 0, v3(0, -400, 0))
        for (let i = 0; i < 3; i++) {
            this.createDish(line1, 1, 1.5 + i)
        }
        let dishes: Dish[] = this.getCoveredDishes(false)
        let dish: Dish = _wheel.getRandomItemInArray(dishes)
        dish.changeCover(true)
        for (let i = 0; i < 3; i++) {
            this.createDish(line2, 2, 1.5 + i)
        }
        this.createDish(line2, 3, 2, false, 1)
        this.createDish(line2, 3, 3, false, 1)
        this.createDish(line2, 3, 2.5, false, 2)
        line2.changeDishState()
        this.bottom.position = v3(0, -1000, 0)
    }

    createLine(type: number = 1, need: number, pos: Vec3): Line {
        let ln: Node = instantiate(this.linePrefabs[type - 1])
        if (!ln) {
            return null
        }
        ln.parent = this.zone
        ln.setPosition(pos)
        let line: Line = ln.getComponent(Line)
        if (!line) {
            return null
        }
        line.init(type, need)
        if (need <= 0) {
            if (!this.isTrial()) {
                this.unlockLine(line)
            } else {
                line.unlock()
            }
        }
        return line
    }

    unlockLine(line: Line) {
        line.unlock()
        let index: number = 0
        for (let i = 0; i < line.full; i++) {
            if (index >= line.full) {
                break
            }
            let n: number = this.getStackNumberByPhase()
            n = Math.min(n, line.full - index)
            this.createDishes(line, n, index + line.full)
            index += n
        }
        line.changeDishState()
        line.smoothMoveDishes('in')
    }

    getCustomerTaskBySeat(seat: number, notIncluded?: number[]): number[] {
        let chance = this.taskChances[seat]
        let r: number = _wheel.getRandomInt(0, 100)
        let n: number = this.servedNumber < 25 ? r < chance[0] ? 1 : (r < (chance[0] + chance[1]) ? 2 : 3) : 3
        let remain: number = this.customerWaitings[seat].length
        // n = remain < 2 ? 3 : (remain < 4 ? (2 < n ? n : 2) : n)
        let tasks: number[] = []
        for (let i = 0; i < n; i++) {
            tasks.push(this.getDishTypeByPhase(notIncluded, false))
        }
        return tasks
    }

    getDishTypeByPhase(notIncluded?: number[], isTask: boolean = false): number {
        let border: number = this.servedNumber < 5 ? 8 : (this.servedNumber < 7 ? 9 : 10)
        let types: number[] = []
        for (let i = 1; i < border + 1; i++) {
            types.push(i)
        }
        if (notIncluded && notIncluded.length < types.length) {
            for (let i = 0; i < notIncluded.length; i++) {
                let n: number = notIncluded[i]
                let index: number = types.indexOf(n)
                if (index >= 0) {
                    types.splice(index, 1)
                }
            }
        }
        let type = isTask ? _wheel.getRandomInt(1, border + 1) : _wheel.getRandomItemInArray(types)

        // if (this.types.length == 0) {
        //     for (let i = 1; i <= border; i++) {
        //         for (let j = 0; j < 3; j++) {
        //             this.types.push(i)
        //         }
        //     }
        // }
        // let r: number = _wheel.getRandomInt(0, this.types.length)
        // let type: number = this.types[r]
        // if (!isTask) {
        //     this.types.splice(r, 1)
        // }

        // let type = 1
        // if (isTask) {
        //     type = _wheel.getRandomInt(1, border + 1)
        // } else {

        // }

        return type
    }

    getCoveredByFloor(floor: number): boolean {
        let r: number = _wheel.getRandomInt(0, 100)
        let border: number = floor < 1 ? 7 : (floor < 2 ? 12 : 18)
        border += this.servedNumber * 0.25
        return r < border
    }

    getStackNumberByPhase(): number {
        let index: number = 0
        let sn: number = this.servedNumber
        if (sn < 3) {
            index = 0
        } else if (sn < 6) {
            index = 1
        } else if (sn < 10) {
            index = 2
        } else if (sn < 15) {
            index = 3
        } else if (sn < 20) {
            index = 4
        } else if (sn < 25) {
            index = 5
        } else if (sn < 30) {
            index = 6
        }
        let chance = this.stackChances[index]
        let r: number = _wheel.getRandomInt(0, 100)
        return r < chance[0] ? 1 : (r < (chance[0] + chance[1]) ? 2 : 3)
    }

    createDishes(line: Line, number: number = 1, index: number = line.full) {
        let nds = this.getDishesByType()
        let notIncluded: number[] = []
        for (let i = 1; i <= this.maxType; i++) {
            let dts = nds[i]
            if (dts && dts.length >= 4) {
                notIncluded.push(i)
            }
        }
        if (number == 2) {
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index, this.getCoveredByFloor(0), 0)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 1, this.getCoveredByFloor(0), 0)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 0.5, this.getCoveredByFloor(1), 1)
        } else if (number == 3) {
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index, this.getCoveredByFloor(0), 0)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 1, this.getCoveredByFloor(0), 0)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 2, this.getCoveredByFloor(0), 0)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 0.5, this.getCoveredByFloor(1), 1)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 1.5, this.getCoveredByFloor(1), 1)
            this.createDish(line, this.getDishTypeByPhase(notIncluded), index + 1, this.getCoveredByFloor(2), 2)
        } else {
            for (let i = 0; i < number; i++) {
                this.createDish(line, this.getDishTypeByPhase(notIncluded), index + i, this.getCoveredByFloor(0), 0)
            }
        }
    }

    createDish(line: Line, type: number = 1, index: number = 0, covered: boolean = false, floor: number = 0): Dish {
        let dn: Node = instantiate(this.dishPrefab)
        if (!dn) {
            return null
        }
        dn.parent = line.zone
        // dn.setSiblingIndex(line.zone.children.length)
        dn.setPosition(line.getDishPos(index))
        let dish: Dish = dn.getComponent(Dish)
        if (!dish) {
            return null
        }
        dish.init(type, index, covered, floor)
        return dish
    }

    createCustomers() {
        // for (let i = 0; i < this.customers.length; i++) {
        //     let tasks = this.getCustomerTaskBySeat(i)
        //     this.createCustomer(1, i, this.getPatienceByChance(tasks.length), tasks)
        // }
        for (let j = 0; j < 10; j++) {
            for (let i = 0; i < this.customers.length; i++) {
                let r: number = _wheel.getRandomInt(-220, 220)
                let rx: number = 0 + r
                let y: number = 200 - j * 10
                let pos: Vec3 = v3(rx, y, 0)
                let customer: Customer = this.createWaitingCustomer(i, pos)
                this.customerWaitings[i].push(customer)
            }
        }

        tween(this)
            .delay(1)
            .call(() => {
                for (let i = 0; i < this.customers.length; i++) {
                    this.waitingMoveToSeat(i, true)
                }
            })
            .start()
    }

    createWaitingCustomer(seat: number = 0, pos: Vec3): Customer {
        let cn: Node = instantiate(this.customerPrefab)
        cn.parent = this.waitings
        cn.position = pos
        let customer: Customer = cn.getComponent(Customer)
        customer.hideAll()
        // let r: number = _wheel.getRandomInt(0, 2)
        // if (r == 0) {
        //     customer.pic.node.scale.multiply3f(-1, 1, 1)
        // }
        return customer
    }

    waitingMoveToSeat(seat: number, served: boolean) {
        let ws = this.customerWaitings[seat]
        let customer: Customer = null
        if (served) {
            customer = ws.pop()
            if (!customer) {
                return
            }
            for (let i = 0; i < ws.length; i++) {
                let wc: Customer = ws[i]
                let wn: Node = wc.node
                tween(wn)
                    .by(.5, { position: v3(0, -10, 0) })
                    .start()
            }
            customer.hideComplain()
        } else {
            let pos: Vec3 = null
            if (ws.length <= 0) {
                pos = v3(-600, 0, 0)
            } else {
                pos = ws[ws.length - 1].node.getPosition()
            }
            customer = this.createWaitingCustomer(seat, pos)
        } 
        let cn: Node = customer.node
        //不和在场客人任务一致
        let notIncluded: number[] = []
        for (let i = 0; i < this.customers.length; i++) {
            let c: Customer = this.customers[i]
            if (!c || !c.sat) {
                continue
            }
            for (let j = 0; j < c.tasks.length; j++) {
                let t: Task = c.tasks[j]
                notIncluded.push(t.type)
            }
        }
        let tasks = this.getCustomerTaskBySeat(seat, notIncluded)
        let ftasks = this.createTasks(tasks)
        this.customers[seat] = customer
        cn.parent = this.seats
        customer.init(ftasks)
        tween(cn)
            .to(.5, { position: v3(-220 + seat * 220, 0, 0) })
            .delay(.1)
            .call(() => {
                customer.showContent(true, () => {
                    customer.sat = true
                    let checked = this.checkCustomer(customer)
                    this.serveCustomer(customer, checked, true)
                })
                tween(customer)
                    .delay(20)
                    .call(() => {
                        if (customer.sat) {
                            customer.changePic(2)
                        }
                    })
                    .start()
            })
            .start()
    }

    createCustomer(type: number = 1, seat: number = 1, dts?: number[]): Customer {
        let cn: Node = instantiate(this.customerPrefab)
        cn.parent = this.seats
        cn.position = v3(600, 0, 0)
        let customer: Customer = cn.getComponent(Customer)
        let tasks: Task[] = this.createTasks(dts)
        customer.init(tasks)
        this.customers[seat] = customer
        tween(cn)
            .to(.5, { position: v3(-220 + seat * 220, 0, 0) })
            .delay(.1)
            .call(() => {
                customer.showContent(true, () => {
                    customer.sat = true
                    let checked = this.checkCustomer(customer)
                    this.serveCustomer(customer, checked, true)
                })
            })
            .start()
        return customer
    }

    createTasks(dts?: number[]): Task[] {
        let tasks: Task[] = []
        if (dts) {
            for (let i = 0; i < dts.length; i++) {
                let type: number = dts[i]
                let task: Task = this.createTask(type)
                tasks.push(task)
            }
        } else {
            let i = 1
            let r: number = _wheel.getRandomInt(0, 10)
            if (r == 0) {
                i = 3
            } else if (0 < r && r <= 3) {
                i = 2
            } else if (3 < r && r <= 9) {
                i = 1
            }
            for (let n = 0; n < i; n++) {
                let type: number = _wheel.getRandomInt(1, 11)
                let task: Task = this.createTask(type)
                tasks.push(task)
            }
        }
        return tasks
    }

    createTask(type: number = 1, amount: number = 1): Task {
        let tn: Node = instantiate(this.taskPrefab)
        let task: Task = tn.getComponent(Task)
        task.init(type, amount)
        return task
    }

    openUnlockPanel() {
        if (this.isTrial()) {
            return
        }
        _resource.openPanel(this.unlockPanel)
    }

    clear(add: boolean = true) {
        if (add) {
            this.propsUse.clear += 1
        }
        tween(this)
            .call(() => {
                this.touchable = false
            })
            .delay(.3)
            .call(() => {
                for (let i = this.served.length - 1; i >= 0; i--) {
                    let dish: Dish = this.served[i]
                    if (dish.removed || dish.merged) {
                        continue
                    }
                    let dn: Node = dish.node
                    this.served.splice(i, 1)
                    tween(dn)
                        .to(.2, { scale: v3(0.1, 0.1, 0.1) })
                        .call(() => {
                            dn.removeFromParent()
                        })
                        .start()
                }
            })
            .delay(.2)
            .call(() => {
                this.moveOthers(-1)
            })
            .delay(.1)
            .call(() => {
                this.touchable = true
            })
            .start()
    }

    refresh() {
        this.propsUse.refresh += 1
        let lines = this.zone.children
        for (let i = 0; i < lines.length; i++) {
            let ln: Node = lines[i]
            let line: Line = ln.getComponent(Line)
            if (line.need > 0) {
                continue
            }
            Tween.stopAllByTarget(line)
            line.smoothMoveDishes('out', () => {
                let dishes = line.zone.children
                for (let j = dishes.length - 1; j >= 0; j--) {
                    let dn: Node = dishes[j]
                    dn.removeFromParent()
                }
                this.unlockLine(line)
            })
        }
    }

    uncover() {
        this.propsUse.uncover += 1
        let lines = this.zone.children
        for (let i = 0; i < lines.length; i++) {
            let ln: Node = lines[i]
            let line: Line = ln.getComponent(Line)
            if (line.need > 0) {
                continue
            }
            let dishes = line.zone.children
            for (let j = dishes.length - 1; j >= 0; j--) {
                let dn: Node = dishes[j]
                let dish: Dish = dn.getComponent(Dish)
                if (!dish.covered) {
                    continue
                }
                dish.changeCover(false)
            }
        }
    }

    unlock() {
        this.propsUse.unlock += 1
        this.curSlot += 1
        this.refreshSlots()
    }

    getCoveredDishes(covered: boolean = true): Dish[] {
        let cdishes: Dish[] = []
        let lines = this.zone.children
        for (let i = 0; i < lines.length; i++) {
            let ln: Node = lines[i]
            let line: Line = ln.getComponent(Line)
            if (line.need > 0) {
                continue
            }
            let dishes = line.zone.children
            for (let j = dishes.length - 1; j >= 0; j--) {
                let dn: Node = dishes[j]
                let dish: Dish = dn.getComponent(Dish)
                if (covered) {
                    if (dish.covered) {
                        cdishes.push(dish)
                    }
                } else {
                    if (!dish.covered) {
                        cdishes.push(dish)
                    }
                }
            }
        }
        return cdishes
    }

    getDishesByType(): Dish[][] {
        let dishes: Dish[][] = []
        let ls = this.zone.children
        for (let i = 0; i < ls.length; i++) {
            let ln: Node = ls[i]
            let line: Line = ln.getComponent(Line)
            let ds = line.zone.children
            for (let j = 0; j < ds.length; j++) {
                let dn: Node = ds[j]
                let dish: Dish = dn.getComponent(Dish)
                let type: number = dish.type
                let dts: Dish[] = dishes[type]
                if (!dts) {
                    dts = []
                }
                dts.push(dish)
                dishes[type] = dts
            }
        }
        return dishes
    }

    playMergeEffect(pos: Vec3, scale: number) {
        let node: Node = instantiate(this.mergeEffect)
        node.parent = this.effects
        node.setPosition(pos)
        node.setScale(v3(scale, scale, scale))
        tween(node)
            .delay(0.25)
            .call(() => {
                node.removeFromParent()
            })
            .start()
    }

    playUncoverEffect(pos: Vec3) {
        let node: Node = instantiate(this.uncoverEffect)
        node.parent = this.effects
        node.setPosition(pos)
        tween(node)
            .delay(0.25)
            .call(() => {
                node.removeFromParent()
            })
            .start()
    }

    pause(): void {
        
    }

    resume(): void {
        
    }

    revive(): void {
        this.propsUse.revive += 1
        this.failSc.node.active = false
        this.gameState = GameState.Gaming
        this.clear(false)
    }

    fail(): void {
        if (this.isInGameState(GameState.Fail)) {
            return
        }
        this.gameState = GameState.Fail
        _player.playSound(AudioName.fail)
        this.failSc.node.active = true
        this.failSc.show(() => {
            _resource.openPanel(_resource.getPanelPrefab(PanelName.FailPanel))
        })
    }

    success(): void {
        if (this.isInGameState(GameState.Success)) {
            return
        }
        this.gameState = GameState.Success
        _resource.openPanel(_resource.getPanelPrefab(PanelName.SuccessPanel))
        _player.playSound(AudioName.success)
        _player.changeNumData('success', _player.daily, 1, 'add')

        let pf = _platform.platform
        if (pf instanceof TT) {
            let priority: number = 999999 - this.passtime / 1000
            let passtime: string = _wheel.secondsToMinuteTime(Math.round(this.passtime / 1000))
            pf.setImRankData(passtime, 1, priority)

            let total = ''
            for (let key in this.propsUse) {
                let n: number = this.propsUse[key]
                total += key + ': ' + n + '    '
            }
            pf.reportAnalytics('Success', {
                passtime: this.passtime / 1000,
                refresh: this.propsUse.refresh,
                clear: this.propsUse.clear,
                uncover: this.propsUse.uncover,
                revive: this.propsUse.revive,
                unlock: this.propsUse.unlock,
                total: total
            })
        }
    }

    restart(): void {
        this.reset()
        this.run()
    }

    reset(): void {
        this.passtime = 0
        this.failSc.node.active = false
        this.level = 0
        this.touchable = true
        this.touched = null
        this.curSlot = this.defSlot
        this.patienceChance = 10
        this.refreshSlots()
        this.clearZone()
        this.clearServed()
        this.clearSeats()
        for (let key in this.propsUse) {
            this.propsUse[key] = 0
        }
    }

    quit(): void {
        this.clearTimers()
        if (this.isInGameState(GameState.Fail)) {
            let pf = _platform.platform
            if (pf instanceof TT) {
                let total = ''
                for (let key in this.propsUse) {
                    let n: number = this.propsUse[key]
                    total += key + ': ' + n + '    '
                }
                pf.reportAnalytics('Fail', {
                    served: this.servedNumber,
                    passtime: this.passtime / 1000,
                    refresh: this.propsUse.refresh,
                    clear: this.propsUse.clear,
                    uncover: this.propsUse.uncover,
                    revive: this.propsUse.revive,
                    unlock: this.propsUse.unlock,
                    total: total
                })
            }
        }
        this.node.destroy()
        _game.game = null
        _player.switchBgm(AudioName.mainbgm)
    }
}


